import crypto from "node:crypto";
import { Part, Stream } from "./_type";
import douyinSign from './douyin_sign';

// Types (these would typically be in separate files)
interface DouyinData {
  aweme_detail: {
    aweme_type: number;
    desc: string;
    images: {
      url_list: string[];
    }[];
    video: {
      play_addr: {
        url_list: string[];
      };
    };
  };
}

export default async (url: string) => {
  if (url.includes("v.douyin.com")) {
    const response = await fetch(url, { redirect: "manual" });
    if (response.status === 302) {
      url = response.headers.get("location") || url;
    }
  }

  const itemIds = url.match(/\/video\/(\d+)/);
  if (!itemIds || itemIds.length < 2) {
    throw new Error("Unable to get video ID");
  }
  const itemId = itemIds[1];

  const cookie = await createCookie();

  const apiUrl = new URL(
    `https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=${itemId}`
  );
  const headers = {
    Cookie: cookie,
    Referer: "https://www.douyin.com/",
    "User-Agent":
      "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36 Edg/87.0.664.66",
  };

  const sign = douyinSign.sign(apiUrl.searchParams.toString(), headers["User-Agent"]);

  apiUrl.searchParams.append("X-Bogus", sign);

  const response = await fetch(apiUrl.toString(), { headers });
  const douyin: DouyinData = await response.json();

  const urlData: Part[] = [];
  let douyinType: "video" | "image";
  let totalSize = 0;

  if (douyin.aweme_detail.aweme_type === 68) {
    douyinType = "image";
    for (const img of douyin.aweme_detail.images) {
      const realURL = img.url_list[img.url_list.length - 1];
      const size = await getSize(realURL, url);
      totalSize += size;
      const ext = getExtension(realURL);
      urlData.push({ url: realURL, size, ext });
    }
  } else {
    douyinType = "video";
    const realURL = douyin.aweme_detail.video.play_addr.url_list[0];
    totalSize = await getSize(realURL, url);
    urlData.push({ url: realURL, size: totalSize, ext: "mp4" });
  }

  const streams: { [key: string]: Stream } = {
    default: {
      parts: urlData,
      size: totalSize,
    },
  };

  return [
    {
      site: "抖音 douyin.com",
      title: douyin.aweme_detail.desc,
      type: douyinType,
      streams,
      url,
    },
  ];
};

const createCookie = async () => {
  const v1 = msToken(107);
  const v2 = ttwid();
  const v3 =
    "324fb4ea4a89c0c05827e18a1ed9cf9bf8a17f7705fcc793fec935b637867e2a5a9b8168c885554d029919117a18ba69";
  const v4 =
    "eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWNsaWVudC1jc3IiOiItLS0tLUJFR0lOIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLVxyXG5NSUlCRFRDQnRRSUJBREFuTVFzd0NRWURWUVFHRXdKRFRqRVlNQllHQTFVRUF3d1BZbVJmZEdsamEyVjBYMmQxXHJcbllYSmtNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVKUDZzbjNLRlFBNUROSEcyK2F4bXAwNG5cclxud1hBSTZDU1IyZW1sVUE5QTZ4aGQzbVlPUlI4NVRLZ2tXd1FJSmp3Nyszdnc0Z2NNRG5iOTRoS3MvSjFJc3FBc1xyXG5NQ29HQ1NxR1NJYjNEUUVKRGpFZE1Cc3dHUVlEVlIwUkJCSXdFSUlPZDNkM0xtUnZkWGxwYmk1amIyMHdDZ1lJXHJcbktvWkl6ajBFQXdJRFJ3QXdSQUlnVmJkWTI0c0RYS0c0S2h3WlBmOHpxVDRBU0ROamNUb2FFRi9MQnd2QS8xSUNcclxuSURiVmZCUk1PQVB5cWJkcytld1QwSDZqdDg1czZZTVNVZEo5Z2dmOWlmeTBcclxuLS0tLS1FTkQgQ0VSVElGSUNBVEUgUkVRVUVTVC0tLS0tXHJcbiJ9";
  return `msToken=${v1};ttwid=${v2};odin_tt=${v3};bd_ticket_guard_client_data=${v4};`;
};

const msToken = (length: number) => {
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  let result = "";
  const randomValues = new Uint8Array(length);
  crypto.randomFillSync(randomValues);
  for (let i = 0; i < length; i++) {
    result += characters.charAt(randomValues[i] % characters.length);
  }
  return result;
};

const ttwid = async () => {
  const body = {
    aid: 1768,
    union: true,
    needFid: false,
    region: "cn",
    cbUrlProtocol: "https",
    service: "www.ixigua.com",
    migrate_info: { ticket: "", source: "node" },
  };

  const response = await fetch(
    "https://ttwid.bytedance.com/ttwid/union/register/",
    {
      method: "POST",
      body: JSON.stringify(body),
      headers: { "Content-Type": "application/json" },
    }
  );

  const setCookie = response.headers.get("Set-Cookie");
  if (setCookie) {
    const match = setCookie.match(/ttwid=([^;]+)/);
    if (match) {
      return match[1];
    }
  }
  throw new Error("douyin ttwid request failed");
};

const getSize = async (url: string, referer: string) => {
  const response = await fetch(url, {
    method: "HEAD",
    headers: { Referer: referer },
  });
  const contentLength = response.headers.get("content-length");
  return contentLength ? parseInt(contentLength, 10) : 0;
};

const getExtension = (url: string): string => {
  const parsedUrl = new URL(url);
  const pathname = parsedUrl.pathname;
  const ext = pathname.split(".").pop();
  return ext || "";
};
